FAQ + Errata (Assignment 1)
This is a new assignment for this year, so there's going to be stuff we discover as we go. This page is a collection of such stuff: errata and clarifications.
Whenever this page contradicts the assignment spec, this page should be considered authoritative.
1 Clarifications
1.1 Monoid laws for union and intersection
Because we don't require your functions to maintain minimality,
there are several well-formed tries that can represent the same dictionary.
For example, both Trie False []
and
Trie False [('a',Trie False [])]
represent the empty dictionary.
For this reason, your Monoid TrieMonoid
instance might not satisfy the monad laws.
Instead, we only require you to satisfy the monad laws up-to dictionary
equivalence.
We say that two well-formed dictionaries \(t\) and \(t'\) are dictionary
equivalent, written \(t \simeq t'\), if \(\forall
\mathit{xs}. \mathtt{check}\ t\ \mathit{xs} = \mathtt{check}\ t'\
\mathit{xs}\). TrieMonoid
only needs to
satisfy the following laws:
In the lecture, this was phrased as "identity up-to pruning", but that amounts to the same thing.
1.2 Description of pick
The comment above the definition of pick
in the code template has the argument order backwards. The type
signature is the argument order you should go with. Thus, whenever
the comment says pick x xs
, pretend it
says pick xs x
.
Here is a debugged comment:
{- pick xs x is a utility function which should satisfy the following properties: - If `elem x xs`, then `pick xs x = (True,ys)` for some `ys` such that `xs` is a permutation of `x:ys`. - If `not(elem x xs)`, then `pick xs x = (False,ys)`, for some `ys` such that `xs` is a permutation of `ys`. This utility function is useful for pulling out specific tiles from a rack. -} pick :: Eq a => [a] -> a -> (Bool,[a]) pick = error "TODO: implement pick"
1.3 Can we assume that all dictionary characters are in [a..z]?
No. The dictionary can contain any Char
.
The default generator only generates characters in the
[a..z]
range for simplicity, but you
should not depend on this behaviour.
If the dictionary happens to contain both lower-case and upper-case characters, these should be treated as distinct letters. (For practical purposes, the user of your program probably wants to ensure that their dictionary has normalised case. But that's not your responsibility: you should treat distinct characters as distinct.)
1.4 Do moves need to touch pre-existing letters?
No. If they touch pre-existing letters, legal words need to be formed. But they do not have to touch. So for example, on the dog board from the spec, a three-letter word could be a legal move.
Note that while the rules of Scrabble only allow moves that either
start at the origin or touch pre-existing words, it is not the
responsibility of moves
or
allMoves
to enforce this rule. In a full
implementation, you would iterate allMoves
over every legal position from which to start a move; this iteration
should be responsible for excluding invalid starting points.
1.5 Definition of a legal move
The assignment spec mentions an important constraint on the set of legal moves that is not in the inline code comments: that moves need to form a word of length >= 2 in the direction of play, and only newly formed words of length >= 2 need to be legal. A word counts as formed if it includes at least one newly placed tile (at least one blue tile in the pictures).
This implies that moves
and
allMoves
should never return words of
length 0 or 1.
On the other hand, filterPlayables
should return words of length 0 or 1 when applicable.
filterPlayables
should only consider which
words can be formed using the tiles on the given Rack
, and not take other constraints arising from the game
rules into consideration.
1.6 trieOrDie clarifications
The bonus marks are 2/100 marks for the final exam.
Obviously, a trie contains a list of subtries, and you need to process that list somehow; iterating through this list is of course allowed. What you shouldn't do is representing dictionaries as lists of strings.
1.7 Dog board typo
A previous version of the spec claimed that in order for "nastier" to be a legal move, "dog" needs to be one. It should have said "or". Only newly formed words—or in the pictures, words containing at least one blue tile—should be considered.
2 FAQ
2.1 What imports are allowed?
You're allowed to import additional functions from the libraries
already imported by the template, and anything else from base or
quickcheck. Or in other words, as long as you can import it
without adding stuff to the .cabal
file you can import it.
2.2 How do blank tile that are already on the board work?
There is no special consideration of Blank
s that are already on the board that you need to
implement. Once a blank tile is on the board, it is permanently
treated as whatever letter the player declared them to be as part of
their move, except for scoring purposes. But we don't consider scoring
in this assignment so we don't need to make any such distinction.
2.3 In what order do the words above the main row come?
From top to bottom. So for example, in order to check whether "proof"
is a legal move on this board:
bed_board :: Board bed_board = Board [(("beds","read"),Nothing), (("",""),Nothing), (("",""),Nothing), (("",""),Nothing), (("",""),Nothing), (("",""),Nothing), (("",""),Nothing), (("",""),Nothing) ]
…the dictionary would have to contain both "proof"
and "bedspread"
, not "sdebpread"
.
2.4 My failing QuickCheck test shrinks forever even though my code doesn't run forever
The shrinker for tries in the assignment is a bit aggressive and tries
arguably too many things.
Try one of these Arbitrary
instances instead:
instance Arbitrary Trie where arbitrary = sized $ genTrie . min 15 shrink (Trie b ts) = Trie b <$> shrinkList (\(x,t) -> curry id x <$> shrink t) ts' where ts' = zip ['a'..'z'] $ snd <$> ts
instance Arbitrary Trie where arbitrary = sized $ genTrie . min 15 shrink (Trie b ts) = Trie b <$> shrinkList (\(x,t) -> curry id x <$> shrink t) ts
instance Arbitrary Trie where arbitrary = sized $ genTrie . min 15 shrink (Trie b ts) = []
If you have a better shrinker, please feel free to share on the forums!